#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <iiadd.h>




#include "udt.h"

#include <openssl/evp.h>  /* required for checksum */

/* max string length returned by checksum */
#define MAX_CHECKSUM_LENGTH 48 + sizeof(short)
#define MAX_ERIS_AT_GET_AT_STRING_LENGTH 400 +  sizeof(short)
//#define MAX_ERIS_VARCHAR_TO_NVARCHAR 400 +  sizeof(short)

#ifndef II_NVARCHAR
#define II_NVARCHAR 27
#endif

/*Easy way to start counting the fod_id's*/
#define UDF_CHECKSUM              		II_OPSTART
#define UDF_ERIS_AT_GET_AT_STRING			II_OPSTART+1


/*Easy way to start counting the fid_id's */
#define UDF_FI_CHECKSUM_VARCHAR      	II_FISTART
#define UDF_FI_ERIS_AT_GET_AT_STRING 	II_FISTART+1



II_STATUS   (*Ingres_trace_function)() = 0;


II_STATUS 
checksum (
    II_SCB          *scb,
    II_DATA_VALUE   *p1,  /* digest_name  */
    II_DATA_VALUE   *p2,  /* text to hash */
    II_DATA_VALUE   *rdv) /* return_text  */
{
	
    EVP_MD_CTX mdctx;
    const EVP_MD *md;
    unsigned char md_value[EVP_MAX_MD_SIZE];
    char digest[33], xx[3], msg[128];
    unsigned short true_length;
    int md_len, i;

    rdv->db_prec     = 0;

    OpenSSL_add_all_digests();
    true_length=*(short* )p1->db_data;
    if (true_length > 32) {
        strncpy(digest, (char *)p1->db_data + sizeof(short), 32);
        digest[32]='\0';
        sprintf(msg,
            "checksum(): digest name commencing '%s' is too long to be valid!", digest);
        us_error(scb, 0x200010, msg);
        return(II_ERROR);
        };

    /*
    ** Confirm p1 is a known digest
    */
    strncpy(digest, (char *)p1->db_data + sizeof(short), true_length);
    digest[true_length]='\0';

    md = EVP_get_digestbyname(digest);

    if(!md) {
        sprintf(msg, "checksum(): Unknown digest '%s'", digest);
        us_error(scb, 0x200011, msg);
        return(II_ERROR);
        };
 /*
    ** The 2nd parameter is passed in a 'varchar' like format regardless of
    ** its true data type. Hence allow for a two character length offset at the
    ** start of the data
    */
    true_length=*(short *)p2->db_data;
    EVP_DigestInit(&mdctx, md);
    EVP_DigestUpdate(&mdctx, (char *)p2->db_data + sizeof(short), true_length);
    EVP_DigestFinal(&mdctx, md_value, &md_len);

    /* Check return length is sane */
    if (md_len * 2 > MAX_CHECKSUM_LENGTH) {
        sprintf(msg,
            "checksum(): Return length of %d exceeds maximumm permitted (%d)",
            md_len * 2, MAX_CHECKSUM_LENGTH
            );
        us_error(scb, 0x200011, msg);
        return(II_ERROR);
    };

    /* And fill in the data...
    ** Note that this is a direct rip-off of the man page...
    */
    
    *((short *) rdv->db_data)=md_len * 2;
    strcpy((char *)rdv->db_data + 2, "");
    strcat(msg,"Result: ");
    for(i = 0; i< md_len; i++) {
        sprintf(xx, "%02x", md_value[i]);
        xx[2]='\0';
        strcat((char *)rdv->db_data + 2, xx);
        strcat(msg,xx);
        };

    return(II_OK);
}; /* checksum */


II_STATUS
eris_at_get_at_string(
   II_SCB          *scb,
   II_DATA_VALUE   *p1, /*@table_name*/
   II_DATA_VALUE   *p2,	/*@column_name*/
   II_DATA_VALUE   *p3, /*@value_str*/
   II_DATA_VALUE   *rdv /* return_text  */
){
	char msg[256],msgtemp[10];
	unsigned int true_length1,true_length2,true_length3,data_length1,data_length2,data_length3,temp_length;
	unsigned short offset;
	offset=sizeof(short);
	short usc0,usc1,usc2,usc3,usc4,usc5,usc6;
	int test1,test2;
	int iLen,i;
	rdv->db_prec  = 0;
	
	true_length1 = *(short*)p1->db_data;
	true_length2 = *(short*)p2->db_data;
	true_length3 = *(short*)p3->db_data;
	
	//if out of the range of value, error!
	if(true_length1>40 || true_length2>40 || true_length3>400 ){
						sprintf(msg,"eris_at_get_at_string(): the length of parametres exceeds maximumm permitted (%d) %d %d %d", 40,
										true_length1,true_length2,true_length3);
 						us_error(scb, 0x200011, msg);
						return(II_ERROR);
	};
	
	data_length1 = true_length1*2;
	data_length2 = true_length2*2;
	
	//if p3 is null
	if(true_length3==0){
		
			iLen += true_length1;
			iLen += true_length2;
			iLen +=	5;
	
			*(short *)rdv->db_data=iLen;
			//copy p1
			//memset( rdv->db_data, 0 , sizeof(rdv->db_length));
				for (i	=	0; i < data_length1; i++) {
      		memcpy( rdv->db_data + offset + i ,  p1->db_data + offset + i, 1);
   		};
   		//memcpy( rdv->db_data + offset ,  p1->db_data + offset, data_length1);
   		//.
  			usc1	=	46;
   			usc0	=	0;
   			memcpy( rdv->db_data + offset + data_length1 ,	&usc1, 1);
   			memcpy( rdv->db_data + offset + data_length1 + 1, &usc0 ,1);
   		//copy p2
 		 	//memcpy( rdv->db_data + offset + data_length1 ,  p2->db_data + offset, data_length2);
 		  for (i=0; i < data_length2; i++) {
  		  	memcpy( rdv->db_data + offset + data_length1 + 2 + i ,  p2->db_data + offset + i, 1);
  		};
 		  //=
  			 usc2	=	61;
  		   memcpy( rdv->db_data + offset + data_length1 + data_length2 + 2,	&usc2, 1);
  		   memcpy( rdv->db_data + offset + data_length1 + data_length2 + 3, &usc0, 1);
 		  //""
  		   usc3	=	34;
  		   memcpy( rdv->db_data + offset + data_length1 + data_length2 + 4,	&usc3, 1);
  		   memcpy( rdv->db_data + offset + data_length1 + data_length2 + 5, &usc0, 1);
  		   memcpy( rdv->db_data + offset + data_length1 + data_length2 + 6,	&usc3, 1);
  		   memcpy( rdv->db_data + offset + data_length1 + data_length2 + 7, &usc0, 1);
  		//;
  		 	usc4	=	59;
  		 	memcpy( rdv->db_data + offset + data_length1 + data_length2 + 8,	&usc4, 1);
  		 	memcpy( rdv->db_data + offset + data_length1 + data_length2 + 9, &usc0 ,1);  
	}
	else{
		
			data_length3=true_length3*2;	
			//copy p1
			//memset( rdv->db_data, 0 , sizeof(rdv->db_length));
				for (i	=	0; i < data_length1; i++) {
      		memcpy( rdv->db_data + offset + i ,  p1->db_data + offset + i, 1);
   		};
   		//memcpy( rdv->db_data + offset ,  p1->db_data + offset, data_length1);
   		//.
  			usc1	=	46;
   			usc0	=	0;
   			memcpy( rdv->db_data + offset + data_length1 ,	&usc1, 1);
   			memcpy( rdv->db_data + offset + data_length1 + 1, &usc0 ,1);
   		//copy p2
 		 	//memcpy( rdv->db_data + offset + data_length1 ,  p2->db_data + offset, data_length2);
 		  for (i=0; i < data_length2; i++) {
  		  	memcpy( rdv->db_data + offset + data_length1 + 2 + i ,  p2->db_data + offset + i, 1);
  		};
 		  //=
  			 usc2	=	61;
  		   memcpy( rdv->db_data + offset + data_length1 + data_length2 + 2,	&usc2, 1);
  		   memcpy( rdv->db_data + offset + data_length1 + data_length2 + 3, &usc0, 1);
   
   		//"
   		usc3=34;
   		memcpy( rdv->db_data + offset + data_length1 + data_length2 + 4,	&usc3, 1);
   		memcpy( rdv->db_data + offset + data_length1 + data_length2 + 5, &usc0 ,1);
   
   		//copy p3 and replace
   		
   		/*
   		for (i=0; i < data_length3;	i++) {
   			memcpy( rdv->db_data + offset + data_length1 + data_length2 + 6 + i,  p3->db_data + offset + i, 1);
   		};
   		*/
   		i=0;
  		temp_length=0;
   		do{
   			test1 = *(p3->db_data + offset + i);
   			memcpy( rdv->db_data + offset + data_length1 + data_length2 + 6 + temp_length, &test1, 1);
   			i++;
   			temp_length++;
   			
   			test2 = *(p3->db_data + offset + i);
				//memcpy( test2,  (int *)(p3->db_data + offset + i), 1);
				memcpy( rdv->db_data + offset + data_length1 + data_length2 + 6 + temp_length, &test2, 1);
				
				//sprintf(msg,"%d %d",test1,test2);	
 				//us_error(scb, 0x200011, msg);
				//return(II_ERROR);
				
   			if(test1==34&&test2==0){
   					temp_length++;
   		   		memcpy( rdv->db_data + offset + data_length1 + data_length2 + 6 + temp_length,  &usc3, 1);
   		   		temp_length++;
   		   		memcpy( rdv->db_data + offset + data_length1 + data_length2 + 6 + temp_length,  &usc0, 1);
   		   		//data_length3 = data_length3 + 2;
   		   		true_length3++;
   		 	};
   		 	i++;
   		 	temp_length++;
   		}while(i < data_length3);
   		
   		
   		
   		data_length3 = true_length3 * 2;
   		//"
  		memcpy( rdv->db_data + offset + data_length1 + data_length2 + data_length3 + 6,	&usc3, 1);//+
  		memcpy( rdv->db_data + offset + data_length1 + data_length2 + data_length3 + 7, &usc0 ,1);//+
   		//;
   		usc4=59;	
   		memcpy( rdv->db_data + offset + data_length1 + data_length2 + data_length3 + 8,	&usc4, 1);//+
   		memcpy( rdv->db_data + offset + data_length1 + data_length2 + data_length3 + 9, &usc0 ,1);//+
   		
   		//length rdv
   		iLen += true_length1;
			iLen += true_length2;
			iLen += true_length3;
			iLen +=	5;
			*(short *)rdv->db_data=iLen; 
			
	};
	return(II_OK);			
};/*eris_at_get_at_string(nvachar,nvarchar,nvarchar)*/


/* Need arrays of 2-byte integers to hold the function argument datatypes
** Here are some examples...
*/

static II_DT_ID  UD_2_INT[]        = {II_INTEGER,  II_INTEGER};
static II_DT_ID  UD_FLOAT_INT[]    = {II_FLOAT,    II_INTEGER};
static II_DT_ID  UD_VC_N_VB[]      = {II_VARCHAR,  II_VBYTE};
static II_DT_ID  UD_VC_N_LVC[]     = {II_VARCHAR,  II_LVCH};
static II_DT_ID  UD_VC_N_LB[]      = {II_VARCHAR,  II_LBYTE};
static II_DT_ID  UD_2_VC[]         = {II_VARCHAR,  II_VARCHAR};
static II_DT_ID  UD_LVC_N_VC[]     = {II_LVCH,     II_VARCHAR};
static II_DT_ID  UD_2_VARBYTE[]    = {II_VBYTE,    II_VBYTE};
static II_DT_ID  UD_2_NVARCHAR[]	 = {II_NVARCHAR,II_NVARCHAR};
static II_DT_ID  UD_3_INT[]	 			 = {II_INTEGER,II_INTEGER,II_INTEGER};
static II_DT_ID  UD_3_NVARCHAR[]	 = {II_NVARCHAR,II_NVARCHAR,II_NVARCHAR};
static II_DT_ID  UD_3_VARCHAR[]	 	 = {II_VARCHAR,II_VARCHAR,II_VARCHAR};


	  	     	
static IIADD_FO_DFN Function_Definitions[]={
    {/* Define new function*/
    II_O_OPERATION,   /*fod_object_type*/
    {"checksum"},     /*fod_name*/
    UDF_CHECKSUM,     /*fod_id*/
    II_NORMAL         /*fod_type*/
    },/* checksum */
    /*
    {
    II_O_OPERATION,   
    {"eris_varchar_to_nvarchar"},     
    UDF_ERIS_VARCHAR_TO_NVARCHAR,     
    II_NORMAL         
    },*/
   {
   	II_O_OPERATION,   /*fod_object_type*/
   	{"eris_at_get_at_string"}, /*fod_name*/
   UDF_ERIS_AT_GET_AT_STRING, /*fod_id*/
   II_NORMAL         /*fod_type*/
   }/* eris_at_get_at_string(nvarchar,nvarchar,nvarchar) */

};


static IIADD_FI_DFN Function_Instances[] = {
    {
    II_O_FUNCTION_INSTANCE,       /* fid_object_type */
    UDF_FI_CHECKSUM_VARCHAR,      /* fid_id*/
    II_NO_FI,                     /* fid_cmplmnt*/
    UDF_CHECKSUM,                 /* fid_opid=fod_id from function definition */
    II_NORMAL,                    /* fid_optype */
    II_FID_F0_NOFLAGS,            /* fid_attributes*/
    0,                            /* fid_wslength*/
    2,                            /* fid_numargs*/
    UD_2_VC,                      /* fid_args, a pointer to an array of datatypes*/
    II_VARCHAR,                   /* fid_result, result is an varchar*/
    II_RES_FIXED,                /* fid_rltype*/
    MAX_CHECKSUM_LENGTH ,         /* fid_rlength */
    0,                            /* fid_rprec */
    checksum,                     /* fid_routine */
    0                             /* lenspec_routine */
    }, /*checksum(varchar, varchar)*/
    {
    II_O_FUNCTION_INSTANCE,       /* fid_object_type */
    UDF_FI_ERIS_AT_GET_AT_STRING, /* fid_id*/
    II_NO_FI,                     /* fid_cmplmnt*/
    UDF_ERIS_AT_GET_AT_STRING,    /* fid_opid=fod_id from function definition */
    II_NORMAL,                    /* fid_optype */
    II_FID_F0_NOFLAGS,            /* fid_attributes*/
    0,                            /* fid_wslength*/
    3,                            /* fid_numargs*/
    UD_3_NVARCHAR,               	/* fid_args, a pointer to an array of datatypes*/
    II_NVARCHAR,                   /* fid_result, result is an integer*/
    II_RES_FIXED,                 /* fid_rltype*/
    MAX_ERIS_AT_GET_AT_STRING_LENGTH,         /* fid_rlength */
    0,                            /* fid_rprec */
    eris_at_get_at_string,        /* fid_routine */
    0       											/* lenspec_routine */
    }, /*eris_at_get_at_string(nvarchar,nvarchar,nvarchar)*/
};

/*{
** Name: IIUDADT_REGISTER   - Add the datatype to the server
**
** Description:
**      This routine is called by the DBMS server to add obtain information to
**  add the datatype to the server.  It simply fills in the provided
**  structure and returns.
**
** Inputs:
**      ui_block                        Pointer to user information block.
**  callback_block          Pointer to an II_CALLBACKS structure
**                  which contains information about INGRES
**                  callbacks which are available.
**
**                  Note that after this routine returns
**                  the address of this block is no longer
**                  valid.  Therefore, this routine must
**                  copy the contents in which it is
**                  interested before returning.
**
** Outputs:
**      *ui_block                       User information block
**
**  Returns:
**      II_STATUS
**  Exceptions:
**      none
**
** Side Effects:
**      none
**
## History:
##      02-Mar-1989 (fred)
##          Created.
##  20-Feb-2001 ( fanra01, gupsh01)
##      Added new ifdef #NEED_THIS, the udadt_register
##      is now moved to nvarchar.c
##
*/

static  IIADD_DEFINITION    register_block =
{
    (IIADD_DEFINITION *) 0,
  (IIADD_DEFINITION *) 0,
    sizeof(IIADD_DEFINITION),
   IIADD_DFN2_TYPE,
    0,
    0,
    0,
    0,
    IIADD_INCONSISTENT, /* Ignore the current datatypes known by the recovery
                    system */
    2,  /* Major id */
    0,  /* minor id */
    33,  /*MAS*/
   "Compatibility UDFs add (checksum)and (rand_seq_gen)",  /*MAS*/
    IIADD_T_FAIL_MASK | IIADD_T_LOG_MASK,
    0,     /* set in register function - MAS */
    0,  /* set to zero as there are no types defined MAS */
    0,  /* set to zero as there are no types defined MAS */
   (sizeof(Function_Definitions)/sizeof(IIADD_FO_DFN)),
    Function_Definitions,
  (sizeof(Function_Instances)/sizeof(IIADD_FI_DFN)),
   Function_Instances
};

II_STATUS
IIudadt_register(IIADD_DEFINITION  **ui_block_ptr ,
                 II_CALLBACKS       *callback_block )
{
    
    register_block.add_count = register_block.add_dt_cnt +
            register_block.add_fo_cnt +
            register_block.add_fi_cnt;
    *ui_block_ptr = &register_block;
 
    /*
    ** Note that after this call, the call back block address will
    ** no longer be valid (the function addresses will, but the block
    ** address itself will not).  Therefore, it is necessary to
   ** copy the values in which we are interested before returning.
   */   
     
  
    if (callback_block && callback_block->ii_cb_version >= II_CB_V1)
    {
    Ingres_trace_function = callback_block->ii_cb_trace;
    if (callback_block->ii_cb_version >= II_CB_V2)
    {
        usc_lo_handler = callback_block->ii_lo_handler_fcn;
        usc_lo_filter = callback_block->ii_filter_fcn;
        usc_setup_workspace = callback_block->ii_init_filter_fcn;
        usc_error = callback_block->ii_error_fcn;
    }
    }
    else
    {
    Ingres_trace_function = 0;
 }
    return(II_OK);
}
